from flask import request
from flask_wtf import FlaskForm
from wtforms import widgets, SelectFieldBase
from wtforms.compat import iteritems, text_type

from apps.a_common.error.exceptions import InvalidParamError
from apps.a_common.log import MyLogger

log = MyLogger('get param')


def get_param(params, key, _type, nullable=False, default=None):
    # 获得key 对应的值，处理params为空的情况
    if not params and nullable:
        return default
    val = params.get(key, None)
    log.info(f'get value by key [{key}], need type: [{_type}], get type {type(val)}')

    # 处理值为空的情况
    if (val is None or val == '') and nullable:
        log.info(f'value is None and nullable, return default [{default}]')
        return default
    elif val is None:
        raise InvalidParamError(fields=f"{key} 参数不能为空")

    # 对值做类型处理
    log.info('has value, try to check type')
    try:
        # 对bool做特殊判断
        if _type == bool and isinstance(val, str) and val.lower() == 'false':
            result = False
        else:
            result = _type(val)
    except ValueError as e:
        log.info('param type error, detail: {}, {}, {}'.format(type(e), e, e.args))
        raise InvalidParamError(fields=f"{key} 参数类型错误")
    except Exception as e:
        log.info('param error, detail: {}, {}'.format(type(e), e, e.args))
        raise InvalidParamError(fields=f"{key} 参数错误")
    else:
        return result

    raise Exception()


def assert_params_by_form(form_class: FlaskForm.__class__) -> dict:
    data = request.args or request.form or request.json
    form: FlaskForm = form_class(data)
    if not form.validate():
        raise InvalidParamError(form.errors)
    return form.data


class MyFlaskForm(FlaskForm):

    @property
    def data(self):
        """ 相比原代码，过滤掉了前端本来就没传过来的值 """
        return dict((name, f.data) for name, f in iteritems(self._fields) if f.raw_data != [])


class ArrayField(SelectFieldBase):
    """
    接受一种类型的列表（如int），不需要choice
    """
    widget = widgets.Select(multiple=True)

    def __init__(self, label=None, validators=None, coerce=text_type, **kwargs):
        super(ArrayField, self).__init__(label, validators, **kwargs)
        self.coerce = coerce

    def process_data(self, value):
        try:
            self.data = list(self.coerce(v) for v in value)
        except (ValueError, TypeError):
            self.data = None

    def process_formdata(self, valuelist):
        try:
            self.data = list(self.coerce(x) for x in valuelist)
        except ValueError:
            raise ValueError(self.gettext('Invalid choice(s): one or more data inputs could not be coerced'))

    def pre_validate(self, form):
        pass
